/*
 * @Author: dadadaXU 1413107032@qq.com
 * @Date: 2024-10-10 14:59:11
 * @LastEditors: dadadaXU 1413107032@qq.com
 * @LastEditTime: 2025-01-05 11:30:16
 * @FilePath: /FileBackup/include/argumentsParser.hpp
 * @Description: 通过第三方库[cxxopts](https://github.com/jarro2783/cxxopts)实现用户命令解析

数据备份软件
Usage:
  filebackup: [OPTION...]

  -b, --backup      数据备份
  -r, --restore     数据恢复
  -v, --view arg    查看备份文件信息
  -i, --input arg   输入文件路径
  -o, --output arg  输出文件路径
  -h, --help        显示此帮助文档

 Backup options:
  -c, --compress     对打包文件压缩
      --path arg     指定过滤路径（正则表达式）
      --type arg     过滤文件类型（可以多个组合）:
                      `-`普通文件 `D`目录文件 `P`管道文件 `B`块设备 `C`字符设备 `S`符号链接
      --name arg     过滤文件名（正则表达式）
      --size arg     过滤文件大小区间(bytes) 例 "0 32768"
      --atime arg    文件访问时间区间
                     例 "1970-01-01 00:00:00 2024-12-31 23:59:59"
      --mtime arg    文件修改时间区间
                     例 "1970-01-01 00:00:00 2024-12-31 23:59:59"
      --ctime arg    文件改变时间区间
                     例 "1970-01-01 00:00:00 2024-12-31 23:59:59"
  -m, --message arg  添加备注信息

 Restore options:
  -a, --metadata      恢复文件的元数据

 */
#pragma once

#include "cxxopts.hpp"
#include <sys/types.h>
#include <string>
#include <iostream>
#include <sstream>
#include <filesystem>
#include <regex>
#include <ctime>
#include <set>

#ifndef SHOW_HELP_WIDTH
#define SHOW_HELP_WIDTH 128
#endif

#ifndef PASSWORD_MINLENGTH
#define PASSWORD_MINLENGTH 6
#endif

/* 测试类的私有成员 */
#ifdef GTEST_API_
#define private public
#define protected public
#endif

/* 文件过滤模式 */
#ifndef FILE_FILTER_MODE
typedef unsigned char FileFilterMode;
#define FILE_FILTER_MODE
#define FILE_FILTER_PATH 1
#define FILE_FILTER_NAME 2
#define FILE_FILTER_TYPE 4
#define FILE_FILTER_SIZE 8
#define FILE_FILTER_ATIME 16
#define FILE_FILTER_MTIME 32
#define FILE_FILTER_CTIME 64
#endif

/* 文件类型 */
#ifndef FILE_TYPE
typedef unsigned char FileType;
#define FILE_TYPE
#define FILE_TYPE_OTHER 0         // 其他文件
#define FILE_TYPE_NORMAL 1        // 普通文件
#define FILE_TYPE_DIRECTORY 2     // 目录文件
#define FILE_TYPE_PIPE 4          // 管道文件
#define FILE_TYPE_SYMBOLIC_LINK 8 // 符号链接
#define FILE_TYPE_HARD_LINK 16    // 硬链接
#endif

#ifndef FILE_MODE_PACK
#define FILE_MODE_PACK 1 // 打包文件
#define FILE_SUFFIX_PACK ".pak"
#define DEFAULT_PACK_FILENAME "my_backup_file"
#define TAR_RECORD_SIZE 512
#define TAR_FILE_PATH 256
#define SIZE_CRC_CODE (sizeof(uint32_t))
#define SIZE_BLOCKNUM (sizeof(uint32_t))
#define SIZE_METADATA (sizeof(struct stat)) // 144 bytes
#define SIZE_OFFSET (sizeof(uint32_t))
#define TAR_PAD_SIZE (TAR_RECORD_SIZE - (TAR_FILE_PATH + SIZE_CRC_CODE + SIZE_BLOCKNUM + SIZE_METADATA))
#define CRC_INIT_VALUE 0xFFFFFFFF
#define CRC_REVERSE_VALUE 0xEDB88320 // 0xEDB88320 = reverse 0x04C11DB7
#define PAK_MESSAGE_LEN (TAR_RECORD_SIZE - (TAR_FILE_PATH + SIZE_CRC_CODE + SIZE_OFFSET))
#endif

#ifndef FILE_MODE_COMPRESS
#define FILE_MODE_COMPRESS 2 // 压缩文件
#define FILE_SUFFIX_COMPRESS ".cmp"
#define DEFAULT_COMPRESS_FILENAME "my_compress_file"
#define COMPRESS_FILE_PATH 256
#define TOKEN_NUM 256
#define BUFFER_SIZE 512
#define SIZE_PAD0 (sizeof(uint32_t))
#define DECODE_MASK 0x80
#endif

/// @brief 判断用户输入信息标识
struct UserFlag
{
    /* 基础功能 */
    bool backup;
    bool restore;
    bool viewInfo;
    bool input;
    bool output;
    bool help;
    /* 数据备份 */
    bool compress;
    bool filterPath;
    bool filterType;
    bool filterName;
    bool filterSize;
    bool atime;
    bool mtime;
    bool ctime;
    bool message;
    /* 数据还原 */
    bool deleteFile;
    bool metadata;
    bool goBack;
};

/// @brief 对文件进行过滤的参数
struct FileFilterArguments
{
    FileFilterMode mode; // 过滤的参数标识
    std::string path;    // 正则表达式
    FileType types;
    std::string name; // 正则表达式
    off_t size_from, size_to;
    time_t atime_from, atime_to;
    time_t mtime_from, mtime_to;
    time_t ctime_from, ctime_to;
};

/// @brief 用户命令解析接口
class IParser
{
public:
    UserFlag userFlag;
    std::filesystem::path inputPath;
    std::filesystem::path outputPath;
    std::filesystem::path viewInfoPath;
    FileFilterArguments fileFilterArguments;
    std::string message;

    /* 参数合法性检查 */
    /// @brief 检查数据备份参数
    virtual bool CheckBackupArguments() = 0;
    /// @brief 检查数据恢复参数
    virtual bool CheckRestoreArguments() = 0;
    /// @brief 检查查看备份文件参数
    virtual bool CheckViewInfoArguments() = 0;

    /// @brief 解析参数列表
    virtual bool Parse(int argc, char **argv) = 0;

    IParser() {}
    virtual ~IParser() {}
};

/// @brief 命令行参数解析
class CArgumentsParser : public IParser
{
private:
    cxxopts::Options options;    // `cxxopts` 库初始化信息
    cxxopts::ParseResult result; // 词法分析结果

    /* 用户输入的原始数据 */
    std::string atime;
    std::string mtime;
    std::string ctime;
    std::string fileSize;
    std::string fileTypes;

    /// @brief 检查正则表达式格式
    /// @param expression 正则表达式
    /// @return 正则表达式格式为 `true`
    bool CheckRegexExpression(std::string expression);

    /// @brief 检查 `inputPath` 与 `outputPath` 路径是否有效
    /// @return 路径存在为 `true`
    bool CheckFilePath();

    /// @brief 检查 `fileTypes` 并 格式化输入文件格式
    /// @return 不能有 '-', 'D', 'P', 'B', 'C', 'S', 'H' 之外的字符
    bool CheckFileType();

    /// @brief 检查 并 格式化输入文件大小区间为 `off_t` 例 "0 32768"
    /// @return 保证 `size_from` <= `size_to`
    bool CheckFileSize();

    /// @brief 检查 并 格式化输入时间为 `time_t`
    /// @param dateTimeStr 原始时间 例 "1970-01-01 00:00:00 2024-12-31 23:59:59"
    /// @param dataTimeFrom 起始时间
    /// @param dataTimeTo 结束时间
    /// @return 保证 `dataTimeFrom` <= `dataTimeTo`
    bool CheckFileTime(std::string dateTimeStr, std::time_t &dataTimeFrom, std::time_t &dataTimeTo);

public:
    bool CheckBackupArguments() override;
    bool CheckRestoreArguments() override;
    bool CheckViewInfoArguments() override;

    CArgumentsParser();
    ~CArgumentsParser();

    bool Parse(int argc, char **argv) override;
};